# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         http_testcase_debuger.py
# Description:  
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2019/11/27 11:38
# -------------------------------------------------------------------------------
import logging
import json
from django.utils import timezone

from apps.HttpAutoTestService.core.extractor import Extractor
from apps.HttpAutoTestService.core.utils import parse_request_args
from apps.HttpAutoTestService.core.validator import Validator

logger = logging.getLogger(__name__)


class HttpTestcaseDebuger(object):

    def __init__(self, http_session=None, http_session_context=None, name=None, url=None, method=None, cookies=None,
                 headers=None, request_data_type=None, json_data=None, form_data=None, expect_result=None):
        '''
        实例化用例调试
        @param http_session:
        @param http_session_context:
        @param name:
        @param url:
        @param method:
        @param cookies:
        @param headers:
        @param request_data_type:
        @param json_data:json请求体内容
        @param form_data:form表单请求体内容
        @param expect_result:
        '''
        self.http_session = http_session
        self.http_session_context = http_session_context
        self.name = name
        self.url = url
        self.method = method
        self.cookies = cookies or {}
        self.headers = headers or json.dumps({})
        self.request_data_type = request_data_type
        self.json_data = json_data or json.dumps({})
        self.form_data = form_data or json.dumps({})
        self.expect_result = expect_result or json.dumps({})

    def debug(self):
        logger.info(f'=============== 调试用例:【{self.name}】===============')
        headers = self.headers if isinstance(self.headers, str) else json.dumps(self.headers, ensure_ascii=False)
        request_data = self.json_data or self.form_data
        request_data = request_data if isinstance(request_data, str) else json.dumps(request_data, ensure_ascii=False)
        testcase_result = {
            "url": self.url,
            "method": self.method,
            "headers": headers,
            "request_data": request_data
        }
        variables_mapping = {**self.http_session_context.session_variables_mapping,
                             **self.http_session_context.output_variables_mapping}
        logger.info(f'测试用例【{self.name}】执行前，全局参数内容：{variables_mapping}')

        # 解析接口测试用例参数
        request_arg = None  # 标记正在解析的参数
        try:
            request_arg = "请求URL"
            self.url = parse_request_args(content=self.url, variables_mapping=variables_mapping)
            logger.info('请求URL：{url}'.format(url=self.url))
            logger.info('请求方法：{method}'.format(method=self.method))

            if not isinstance(self.cookies, dict):
                self.cookies = {}

            request_arg = "Headers参数"
            self.headers = parse_request_args(content=self.headers, variables_mapping=variables_mapping)
            self.headers = json.loads(self.headers)
            logger.info('请求头信息：{headers}'.format(headers=self.headers))

            request_arg = "Body参数"
            self.form_data = parse_request_args(content=self.form_data, variables_mapping=variables_mapping)
            self.form_data = json.loads(self.form_data)

            self.json_data = parse_request_args(content=self.json_data, variables_mapping=variables_mapping)
            self.json_data = json.loads(self.json_data)
            logger.info('请求体参数类型：{request_data_type}'.format(request_data_type=self.request_data_type))
            logger.info('请求体参数：{request_data}'.format(request_data=self.form_data or self.json_data))

            # 期望结果
            request_arg = "期望结果"
            self.expect_result = parse_request_args(content=self.expect_result, variables_mapping=variables_mapping)
            self.expect_result = json.loads(self.expect_result)
            logger.info('期望结果：{expect_result}'.format(expect_result=self.expect_result))
        except Exception as e:
            testcase_result.update({
                "actual_status_code": 0,
                "actual_response_data": "",
                "execute_time": timezone.now(),
                "elapsed_ms": 0,
                "status": "FAIL",
                "failure_reason": f"解析接口测试用例参数【{request_arg}】异常，异常原因：{repr(e)}"
            })
            return testcase_result

        request_kwargs = dict(
            cookies=self.cookies,
            headers=self.headers,
            data=self.form_data,
            json=self.json_data
        )

        resp_obj = self.http_session.request(method=self.method, url=self.url, **request_kwargs)
        # 获取HttpSession对象request方法返回数据的keys
        response_keys = resp_obj["response"].keys()
        # 获取请求返回数据实际存储的key
        actual_response_key = list(set(response_keys).intersection(['results', 'text', 'content']))[0]
        if isinstance(resp_obj["response"][actual_response_key], str):
            actual_response_data = resp_obj["response"][actual_response_key]
        else:
            actual_response_data = json.dumps(resp_obj["response"][actual_response_key], ensure_ascii=False)
        logger.info(f'响应结果：{actual_response_data}')

        if resp_obj["response"]["cookies"]:
            # 获取响应cookies
            cookies = resp_obj["response"]["cookies"].get_dict()
            # 将cookies更新到session中
            self.http_session.cookies.update(cookies)

        try:
            if isinstance(self.expect_result, dict) and self.expect_result.get("output"):
                # 创建提取器
                extractor = Extractor()
                # 统一格式化测试用例的输出变量output
                extractor.uniform_output(self.expect_result.get("output"))
                # 提取输出变量值
                output_mapping = extractor.extract_output(resp_obj=resp_obj["response"])
                # 将提取的输出变量值更新到http_session_context实例的update_output_variables变量中
                self.http_session_context.update_output_variables(variables_mapping=output_mapping)

            variables_mapping = {**self.http_session_context.session_variables_mapping,
                                 **self.http_session_context.output_variables_mapping}
            logger.info(f'测试用例【{self.name}】执行后，全局参数内容：{variables_mapping}')
        except Exception as e:
            logger.error(f"提取输出变量值异常，异常原因：{repr(e)}")

        # 创建校验器
        validator = Validator()
        if isinstance(self.expect_result, dict) and not self.expect_result.get("validate", None):
            self.expect_result["validate"] = {"check": "status_code", "comparator": "eq", "expect": 200}

        # 统一格式化测试用例的校验变量validate
        validator.uniform_validate(self.expect_result.get("validate"))

        validate_pass, failure_reason = validator.validate(resp_obj=resp_obj["response"])

        testcase_result = {
            "url": self.url,
            "method": self.method,
            "headers": json.dumps(self.headers, ensure_ascii=False),
            "request_data": json.dumps(self.json_data or self.form_data, ensure_ascii=False),
            "actual_status_code": resp_obj["response"]["status_code"],
            "actual_response_data": actual_response_data,
            "execute_time": resp_obj["stat"]["execute_time"],
            "elapsed_ms": resp_obj["response"]["elapsed"],
            "status": validate_pass
        }
        if validate_pass == 'FAIL':
            logger.warning('用例调试未通过；原因：{failure_reason}'.format(failure_reason=failure_reason))
            testcase_result.update({
                'failure_reason': json.dumps(failure_reason, ensure_ascii=False)
            })

        return testcase_result
